package com.nvwa.coding.util.json;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JsonUtil {
    private static final Logger logger = LoggerFactory.getLogger(JsonUtil.class);

    /**
     * 序列化排除密码过滤器
     */
    public static final String PASSWORD_FILTER = "passwordFilter";
    /**
     * 序列化排除密码过滤器中定义排除的对象属性
     */
    public static final Set<String> PASSWORD_FILTER_FIELDS = new HashSet<>(Arrays.asList("password", "confirmPassword", "newPassword"));

    private static final ObjectMapper MAPPER_DEFAULT = new ObjectMapper();
    private static final ObjectMapper MAPPER_EXCLUDE_PWD = new ObjectMapper();
    private static final ObjectMapper MAPPER_EXCLUDE_NULL = new ObjectMapper();

    static {
        /**
         * 属性过滤，对对象中有密码字典进行过滤
         * 使用方式：需要序列化排除密码的类上增加注解@JsonFilter(JsonUtils.PASSWORD_FILTER)
         */
        SimpleFilterProvider pwdFilterProvider = new SimpleFilterProvider()
                .addFilter(PASSWORD_FILTER, SimpleBeanPropertyFilter.serializeAllExcept(PASSWORD_FILTER_FIELDS))
                .setDefaultFilter(SimpleBeanPropertyFilter.serializeAll());
        //配置默认过滤器，否则在序列化有 @JsonFilter 注解的类时，会报错
        SimpleFilterProvider defaultFilterProvider = new SimpleFilterProvider()
                .setDefaultFilter(SimpleBeanPropertyFilter.serializeAll());


        MAPPER_EXCLUDE_PWD.setFilterProvider(pwdFilterProvider);

        MAPPER_EXCLUDE_NULL.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        MAPPER_EXCLUDE_NULL.setFilterProvider(defaultFilterProvider);

        MAPPER_DEFAULT.setFilterProvider(defaultFilterProvider);
        //反序列化时忽略未知字段
        MAPPER_DEFAULT.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    public static String toString(Object obj) {
        return toString(MAPPER_DEFAULT, obj);
    }

    public static String toStringExcludeNull(Object obj) {
        return toString(MAPPER_EXCLUDE_NULL, obj);
    }

    /**
     * 过滤密码相关字段，需要在实体类加<code>@JsonFilter("passwordFilter")</code>注解
     *
     * @param obj
     * @return
     * @see #MAPPER_EXCLUDE_PWD
     */
    public static String toStringExcludePwd(Object obj) {
        return toString(MAPPER_EXCLUDE_PWD, obj);
    }

    private static String toString(ObjectMapper _mapper, Object obj) {
        if (obj == null) {
            return null;
        }
        if (obj.getClass() == String.class) {
            return (String) obj;
        }
        try {
            return _mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            logger.error("---JsonUtils---toString---json序列化出错：" + obj, e);
            return null;
        }
    }

    public static <T> T toBean(String json, Class<T> tClass) {
        if (null == json) {
            return null;
        }
        try {
            return MAPPER_DEFAULT.readValue(json, tClass);
        } catch (IOException e) {
            logger.error("---JsonUtils---toBean---json解析出错：" + json, e);
            return null;
        }
    }

    public static <T> T mapToBean(LinkedHashMap<String, Object> map, Class<T> tClass) {
        if (null == map) {
            return null;
        }
        try {
            return MAPPER_DEFAULT.convertValue(map, tClass);
        } catch (Exception e) {
            logger.error("---JsonUtils---mapToBean---解析出错：" + map, e);
            return null;
        }
    }

    public static <T> T toBeanIgnoreUnknownProperty(String json, Class<T> tClass) {
        if (null == json) {
            return null;
        }
        try {
            return MAPPER_DEFAULT.readValue(json, tClass);
        } catch (IOException e) {
            logger.error("---JsonUtils---toBean---json解析出错：" + json, e);
            return null;
        }
    }

    public static <E> List<E> toList(String json, Class<E> eClass) {
        if (null == json) {
            return null;
        }
        try {
            return MAPPER_DEFAULT.readValue(json,
                    MAPPER_DEFAULT.getTypeFactory().constructCollectionType(List.class, eClass));
        } catch (IOException e) {
            logger.error("---JsonUtils---toList---json解析出错：" + json, e);
            return null;
        }
    }

    public static <K, V> Map<K, V> toMap(String json, Class<K> kClass, Class<V> vClass) {
        if (null == json) {
            return null;
        }
        try {
            return MAPPER_DEFAULT.readValue(json,
                    MAPPER_DEFAULT.getTypeFactory().constructMapType(Map.class, kClass, vClass));
        } catch (IOException e) {
            logger.error("---JsonUtils---toMap---json解析出错：" + json, e);
            return null;
        }
    }

    public static <T> T nativeRead(String json, TypeReference<T> type) {
        if (null == json) {
            return null;
        }
        try {
            return MAPPER_DEFAULT.readValue(json, type);
        } catch (IOException e) {
            logger.error("---JsonUtils---tonativeRead---json解析出错：" + json, e);
            return null;
        }
    }
}
